package org.seeplnframework.skyeye.common.plugin;

import net.bytebuddy.asm.Advice;
import net.bytebuddy.build.Plugin;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.matcher.ElementMatchers;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * @author ly
 * @date 2022/12/28
 */
public abstract class EnhanceSpecifyAnnotationPlugin<T extends Annotation, A> implements Plugin {

    private final Class<T> annotation;

    private final Class<A> advisor;

    public EnhanceSpecifyAnnotationPlugin() {
        Type[] types = ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments();
        this.annotation = (Class<T>) types[0];
        this.advisor = (Class<A>) types[1];
    }

    @Override
    public DynamicType.Builder<?> apply(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassFileLocator classFileLocator) {
        return builder.visit(Advice.to(advisor).on(ElementMatchers.isAnnotatedWith(ElementMatchers.named(annotation.getName()))));
    }

    @Override
    public boolean matches(TypeDescription description) {
        return description
                .getDeclaredMethods()
                .stream()
                .anyMatch(it -> it.getDeclaredAnnotations().isAnnotationPresent(annotation));
    }

    @Override
    public void close() throws IOException {
        //do nothing
    }
}
